package service

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/dysmsapi"
	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
	smsClient "github.com/yunpian/yunpian-go-sdk/sdk"
	"gsms/internal/conf"
	"gsms/pkg/mcontext"
	"strconv"
	"strings"

	t_errs "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
	sms "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sms/v20210111"
)

type SmsVendor interface {
	sendCode(context.Context, string, string) error
	getName() string
}

type YunPian struct {
	client smsClient.YunpianClient
}

type Aliyun struct {
	client *dysmsapi.Client
}

type DummyVendor struct{}

func (v YunPian) sendCode(ctx context.Context, phone string, code string) error {
	text := fmt.Sprintf("【比邻比利】您的验证码是%v，5分钟内有效，为保证账号安全，请勿泄漏！", code)
	param := smsClient.NewParam(2)
	param[smsClient.MOBILE] = phone
	param[smsClient.TEXT] = text
	log := mcontext.Logger(ctx)
	if r := v.client.Sms().SingleSend(param); !r.IsSucc() {
		log.Error(r.Msg)
		return errors.New(r.Msg)
	}
	return nil
}

func (v YunPian) getName() string {
	return "yunpian"
}

func (v Aliyun) sendCode(ctx context.Context, phone string, code string) error {
	request := dysmsapi.CreateSendSmsRequest()
	request.Scheme = "https"
	request.PhoneNumbers = phone
	request.SignName = "比邻比利"
	request.TemplateCode = "SMS_205585394"
	params := struct {
		Code    string `json:"code"`
		Minutes string `json:"minutes"`
	}{Code: code,
		Minutes: strconv.Itoa(5),
	}
	ps, _ := json.Marshal(params)
	request.TemplateParam = string(ps)

	log := mcontext.Logger(ctx)
	response, err := v.client.SendSms(request)
	log.Info(fmt.Sprintf("response is %#v", response))
	if err != nil {
		return err
	}
	if response.Code != "OK" {
		return fmt.Errorf("发送短信失败: %s", response.Message)
	}
	return nil
}

func (v Aliyun) getName() string {
	return "aliyun"
}

func (v DummyVendor) sendCode(ctx context.Context, phone string, code string) error {
	log := mcontext.Logger(ctx)
	log.Info(fmt.Sprintf("sent code %v to %v", code, phone))
	return nil
}

func (v DummyVendor) getName() string {
	return "dummy"
}

type TencentSms struct {
	client           *sms.Client
	sdkAppId         string
	signName         string
	templateIDVerify string
}

func NewTencentSmsClient(secretId, secretKey, region, sdkAppId, signName, templateIDVerify string) (*TencentSms, error) {
	credential := common.NewCredential(secretId, secretKey)
	cpf := profile.NewClientProfile()
	client, err := sms.NewClient(credential, region, cpf)
	if err != nil {
		return nil, err
	}
	return &TencentSms{client: client, sdkAppId: sdkAppId, signName: signName, templateIDVerify: templateIDVerify}, nil
}

func (v *TencentSms) getName() string {
	return "tencent_cloud"
}

func (v *TencentSms) sendCode(ctx context.Context, phone, code string) error {
	if !strings.HasPrefix(phone, "+86") {
		if strings.HasPrefix(phone, "86") {
			phone = fmt.Sprintf("+%s", phone)
		} else {
			phone = fmt.Sprintf("+86%s", phone)
		}
	}
	request := sms.NewSendSmsRequest()
	request.SmsSdkAppId = common.StringPtr(v.sdkAppId)
	request.SignName = common.StringPtr(v.signName)
	request.TemplateId = common.StringPtr(v.templateIDVerify)
	request.PhoneNumberSet = common.StringPtrs([]string{phone})
	request.TemplateParamSet = common.StringPtrs([]string{code})
	// 发送短信
	log := mcontext.Logger(ctx)
	resp, err := v.client.SendSms(request)
	if _, ok := err.(*t_errs.TencentCloudSDKError); ok {
		log.Info(fmt.Sprintf("短信发送失败: %v", err))
	}
	if err != nil {
		return err
	}
	log.Info(fmt.Sprintf("%v", resp))
	return nil
}

type VendorFactory struct{}

func (fact VendorFactory) CreateVendor(c *conf.Config) (SmsVendor, error) {
	switch c.SMS.VendorName {
	case VendorNameYunPian:
		// 云片SDK的坑：在New的时候Timeout值会乘以time.Second
		smsClient.DefOnlineConf.Http.Timeout = 5
		return YunPian{client: smsClient.New(c.SMS.APIKey)}, nil
	case VendorNameAliyun:
		client, _ := dysmsapi.NewClientWithAccessKey(c.SMS.RegionId, c.SMS.AccessKeyId, c.SMS.AccessKeySecret)
		return Aliyun{client: client}, nil
	case VendorNameTencentCloud:
		client, err := NewTencentSmsClient(c.SMS.AccessKeyId, c.SMS.AccessKeySecret, c.SMS.RegionId, c.SMS.APIKey, c.SMS.SignName, c.SMS.TemplateIDVerify)
		if err != nil {
			return nil, err
		}
		return client, nil
	case VendorNameDummy:
		return DummyVendor{}, nil

	}
	return nil, fmt.Errorf("%v is not supported to construct a vendor", c.SMS.VendorName)
}
